local require = require
local setmetatable = setmetatable
local error = error
local string = string
local json = require 'cjson'
local table = table
local pairs = pairs
local type = type
local ngx = ngx
local tonumber = tonumber
local tostring = tostring
local io = io
local math = math
local os = os
local pcall = pcall
local ffi = require("ffi")
package.cpath = "/opt/xyitech/jieyan-backend-production/xbit/?.so"
local xbit = require("xbit")
module(...)

 _int32_t = 0
 _int16_t = 1
 _float = 2
 _uint32_t = 4
 _uint16_t = 5
 _uint8_t = 6
 _int8_t = 7

_VERSION = '0.1'
_SHA1_KEY = 'tuO4X!7^sSz*JiK&$GZW95tDHg!gmGGJ'

_ALTNATIVE_CHANNEL = "debug"

function each(list,iterator)
    for v,k in pairs(list)
    do
        iterator(k)
    end

end

function map(list,iterator)
    for k,v in pairs(list)
        do
        list[k]=iterator(v)
    end
    return list
end

function filter(list,iterator)
    local a = {}
    for k,v in pairs(list) do
        a[k]= iterator(v)
    end
    return a
end


function find(list,iterator)
    local a = {}
    for k,v in pairs(list)
        do
        a[k]= iterator(v)
    end
    for k,v in pairs(list)
        do
        if(a[k])then
            return a[k]
    end
    end
end

function getChannel(prefix)
    -- ngx.log(ngx.ERR, _ALTNATIVE_CHANNEL)
    -- ngx.log(ngx.ERR, ngx.quote_sql_str('1 or 1=1'))
    if _ALTNATIVE_CHANNEL then
        return prefix .. "_" .. _ALTNATIVE_CHANNEL
    end
    return prefix
end

function trim(s)
  return string.match(s,'^()%s*$') and '' or string.match(s,'^%s*(.*%S)')
end

function split(s, p)
    local rt= {}
    string.gsub(s, '[^' .. p .. ']+', function(w) table.insert(rt, w) end)
    return rt
end 


function tableLength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

--reduce({1,2,3}, function(mem, n)  return mem + n end, 0)
function reduce(t, iterator, memo)
    for k,v in pairs(t) do
        memo = iterator(memo, v)
    end
    return memo
end

function append(t1, t2)
    local t = {}
    for k,v in pairs(t1) do
        table.insert(t, v)
    end
    for k,v in pairs(t2) do
        table.insert(t, v)
    end
    return t
end

function arraySlice(t, s, len)
    local res = {}
    local n = 0
    for k,v in pairs(t) do
        if k >= s and k <= s + len then
            res[#res + 1] = v
        end
    end
    return res
end

function uniq(t)
    local tmp = {}
    for _,v in pairs(t) do
        tmp[v] = 0
    end
    t = {}
    for k,_ in pairs(tmp) do
        table.insert(t, k)
    end
    return t
end

function contains (tab, val)
    for index, value in pairs (tab) do
        if value == val then
            return true
        end
    end
    return false
end

function getIpDetails(ip)
    local msg = ip
    local http = require "http"
    local ok, code, _, _, body  = http:request {
        url = "http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=" .. ip .. "&format=json",
        timeout = 6000,
        method = "GET",
    }
    if body then
        body = json.decode(body)
        msg = msg .." : " .. body.city
    end
    return msg
    -- body
end


ffi.cdef[[
unsigned long compressBound(unsigned long sourceLen);
int compress2(uint8_t *dest, unsigned long *destLen,
        const uint8_t *source, unsigned long sourceLen, int level);
int uncompress(uint8_t *dest, unsigned long *destLen,
    const uint8_t *source, unsigned long sourceLen);
struct timeval {
        long int tv_sec;
        long int tv_usec;
    };
int gettimeofday(struct timeval *tv, void *tz);
]]

function zlib(txt)
    local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
    local n = zlib.compressBound(#txt)
    local buf = ffi.new("uint8_t[?]", n)
    local buflen = ffi.new("unsigned long[1]", n)
    local res = zlib.compress2(buf, buflen, txt, #txt, 9)
    return ffi.string(buf, buflen[0])
end

function gettimeofday()
    local tm = ffi.new("struct timeval");
    ffi.C.gettimeofday(tm, nil);
    local sec = tostring(tonumber(tm.tv_sec));
    local usec = tostring(tonumber(tm.tv_usec));
    for i = string.len(usec), 5 do
        usec = '0' .. usec
    end 
    return sec .. usec;
end

function deepcopy(t)
    local copy
    if type(t) == 'table' then
        copy = {}
        for k,v in pairs(t) do
            copy[deepcopy(k)] = deepcopy(v)
        end
        --setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = t
    end
    return copy
end


function timeString(stamp)
    stamp = stamp or os.time()
    return os.date("%Y-%m-%y %X", stamp)
end
-- "2017-04-17 00:00:00"
function timeStringToStamp(stamp)
    local p = "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"
    local year, month, day, hour, min, sec = stamp:match(p)
    return os.time({day=day,month=month,year=year,hour=hour,min=min,sec=sec})
end

local char, gsub = string.char, string.gsub
local function _(hex) return char(tonumber(hex, 16)) end

function decodeURI(s)
    s = gsub(s, '%%(%x%x)', _)
    return s
end


function get_uid()
    return ngx.encode_base64(os.time() + math.random())
end

function get_token(fid)
    local now = os.time()
    local key  = fid .. '#' .. now
    return ngx.encode_base64(key .. '#' .. ngx.hmac_sha1(_SHA1_KEY,key))
end

function get_control_token(fid)
    local token = ngx.hmac_sha1(_SHA1_KEY, fid .. os.time() / 86400)
    return string.sub(tostring(tonumber(string.sub(token, 0, 6), 16)), 0, 6)
end

function parse_token(token)
    local s = ngx.decode_base64(token)
    ngx.log(ngx.ERR, s)
    if not s then
        return nil
    end
    local items = split(s, '#')

    if #items < 3 then
        return nil
    end
    local key = items[1] .. "#" .. items[2]

    if ngx.hmac_sha1(_SHA1_KEY, key) ~= items[3] then
        return nil
    end
    if os.time() - tonumber(items[2]) > 60000 then
        return nil
    end
    return tonumber(items[1])
end

function zero_clock_of_day(timestamp)
    timestamp = timestamp or os.time()
    local date = os.date("*t", timestamp)
    -- ngx.log(ngx.ERR, json.encode(date))
    local zero = os.time({day=date.day, month=date.month,year=date.year, hour=0, minute=0, second=0})
    return zero
end

function zero_clock_of_month(timestamp)
    timestamp = timestamp or os.time()
    local date = os.date("*t", timestamp)
    local zero = os.time({day=1, month=date.month,year=date.year, hour=0, minute=0, second=0})
    return zero
end

function zero_clock_of_sunday(timestamp)
    timestamp = timestamp or os.time()
    local date = os.date("%w", timestamp)
    local zero = zero_clock_of_day(timestamp)

    return zero - date * 86400
end

function zero_clock_of_year(timestamp)
    timestamp = timestamp or os.time()
    local date = os.date("*t", timestamp)
    local zero = os.time({day=1, month=1,year=date.year, hour=0, minute=0, second=0})
    return zero
end

function withinLast12Monthes(dateTable, checkDateTable)
    if not checkDateTable then
        checkDateTable = os.date("*t")
    end
    local monthDiff = (checkDateTable.year - dateTable.year) * 12 + (checkDateTable.month - dateTable.month)
    return 0 <=  monthDiff and monthDiff <= 12 , monthDiff
end


function withinLast30Days(dateTable, checkDateTable)
    if not checkDateTable then
        checkDateTable = os.date("*t")
    end
    local timeDiff = (os.time(checkDateTable) - os.time(dateTable))/ 86400
    return timeDiff >= 0 and timeDiff <= 30
end

function table2uri(tb)
    local str = nil
    local key_table = {}

    for key,_ in pairs(tb) do  
        table.insert(key_table,key)  
    end  
    --对所有键进行排序  
    table.sort(key_table)
    ngx.log(ngx.ERR, json.encode(key_table))
    for _,key in pairs(key_table) do
        if not str then
            str = escapeUri(key) .. "=" .. escapeUri(tb[key])
        else 
            str = str .. "&" .. escapeUri(key) .. "=" .. escapeUri(tb[key])
        end
    end
    return str
end

function escapeUri(str) 
    local res = ngx.escape_uri(str)
    -- res = string.gsub(res, "+", "%%20")
    -- res = string.gsub(res, "*", "%%2A")
    -- res = string.gsub(res, "%%7E", "~")
    return res
end

function unescapeUri(str)
    local res = ngx.unescape_uri(str)
    -- res = string.gsub(res, "%%20", "+")
    -- res = string.gsub(res, "%%2A", "*")
    -- res = string.gsub(res,  "~", "%%7E")
    return res
end

function isoTime(timestamp)
    return os.date("%Y-%m-%dT%H:%M:%S", timestamp - 8 * 3600)
    -- body
end

function date(timestamp)
    return os.date("%Y-%m-%d", timestamp)
end

function secondsToMS(sec)
    local div = math.floor(sec / 60)
    return string.format("%02d", div) ..":" ..string.format("%02d", sec - div * 60)
end

function remainBatteryPercent(x, inflight)
    local y = 0
    x = x / 1000 or 0
    if inflight then
        -- ngx.log(ngx.ERR, inflight)
        x = -0.011746 * x ^ 3 + 1.600855 * x ^ 2- 71.6319 * x + 1098.1
    end
    if x >= 44 then
        y = 0.090336783643 * x ^ 4- 16.93363513 * x ^ 3 +  1187.80662 * x ^ 2 - 36935.983 * x + 429477.245
    else
        y = -0.00441217 * x ^ 3 - 0.702 * x ^ 2 - 34.685 * x + 546.75
    end
    return math.max(math.min(100, y), 0)
end

function remainBatteryTimeBeforeFlight(x, g, G, g1)

    local y = remainBatteryPercent(x)

    return remainBatteryTimeBeforeFlightWithY(y, g, G, g1)
end

function remainBatteryTimeBeforeFlightWithY(y, g, G, g1)
    y = y / 100
    g1 = tonumber(g1)
    if not g1 or g1 == 0 then
        g1 = 1
    end
    g = g or 11.2
    G = G or 1
    g = g * 1000
    G = G * 1000
    return 3600 * 1 * 945 * y * (-1 / 3000 * (g + G) * g1 + 14) / (g + G)
end

function timeDistanceBetweenRoutePoint(p, p1)
    local distance = ((p.lat * 1e5 - p1.lat * 1e5)^2 + (p.lng * 1e5 - p1.lng * 1e5)^2)^0.5
    
    if not p1.v or p1.v == 0 then p1.v = 0.001 end
    local time = distance / p1.v
    local rateOfClimb = (p1.alt - p.alt) / time
    return time, distance, rateOfClimb
end

function distance(p, p1)
    local plng = p.lng or p.lon
    local p1lng = p1.lng or p1.lon

    return ((p.lat * 1e5 - p1.lat * 1e5)^2 + (plng * 1e5 - p1lng * 1e5)^2)^0.5
end

function distanceRaw(p, p1)
    local plng = p.lng or p.lon
    local p1lng = p1.lng or p1.lon

    return ((p.lat - p1.lat)^2 + (plng - p1lng)^2)^0.5
end

function flightTimeDistanceGivenP(p, route, heightDiff)
    local restT, restD = 0, 0
    local shortestDistance, shortestDistanceTime, index = -1, 0, 1
    local restOfRoute = {}
    for i,v in pairs(route) do
        local t, d = timeDistanceBetweenRoutePoint(p, v)
        if shortestDistance > 0 then
            if d < shortestDistance then
                shortestDistance = d
                ngx.log(ngx.ERR, "shortestDistance:", d)
                shortestDistanceTime = t
                index = i
            end
        else
            shortestDistance = d
        end
    end
    restT = restT + shortestDistanceTime
    restD = restD + shortestDistance
    ngx.log(ngx.ERR, restD)
    local ifPassPoint = ifPassPoint(p, route[index], route[index + 1])
    index = ifPassPoint and index + 1 or index
    for i = index,#route - 1 do
        local p = route[i]
        local p1 = route[i + 1]
        -- ngx.log(ngx.ERR, route[i])
        local time, distance = timeDistanceBetweenRoutePoint(p, p1)
        -- ngx.log(ngx.ERR, time)
        restT = restT + time
        restD = restD + distance
    end
    ngx.log(ngx.ERR, restD)
    local t,d = calcEndPointTimeDistance(route[#route], heightDiff)
    ngx.log(ngx.ERR, restD)
    restT = restT + t
    restD = restD + d
    return restT, restD
end

function getNextPoint(p, route)
    local shortestDistance, shortestDistanceTime, index = -1, 0, 1
    local restOfRoute = {}
    for i,v in pairs(route) do
        local t, d = timeDistanceBetweenRoutePoint(p, v)
        if shortestDistance > 0 then
            if d < shortestDistance then
                shortestDistance = d
                ngx.log(ngx.ERR, "shortestDistance:", d)
                shortestDistanceTime = t
                index = i
            end
        else
            shortestDistance = d
        end
    end
    local ifPassPoint = ifPassPoint(p, route[index], route[index + 1])
    index = ifPassPoint and index + 1 or index
    return index
end

function ifPassPoint(planeP, targetP, nextP)
    if not nextP then return false end
    return vectorIncludedAngle(getVectorFromPoints(planeP, targetP), getVectorFromPoints(targetP, nextP)) > 90
end

function getVectorFromPoints(p, p1)
    return {p1.lat - p.lat, p1.lng - p.lng}
end

function vectorModule(vec)
    local value = 0
    for k,v in pairs(vec) do
        value = value + v^2
    end
    return math.sqrt(value)
end

function vectorDotProduct(vec1, vec2)
    local value = 0
    for i=1,#vec1 do
        value = value + vec1[i]*vec2[i]
    end
    return value
end



function unitVector(p, p1)
    local w = {}
    local d = distanceRaw(p, p1)
    w[1] = (p1.lat - p.lat) / d
    w[2] = (p1.lng - p.lng) / d
    return w
end

function addOffset(p, offsets)
    local _p = {}
    for k,v in pairs(p) do
        _p[k] = p[k]
        if offsets[k] then
             _p[k] = _p[k] + offsets[k]
        end
    end
    return _p
end

function xProduct(p, p1)
    return p[1] * p1[2] - p1[1] * p[2]
end
function getSubRoutes(route, _w)
    local subRoutes = {{}, {}, {}, {}}
    if #route < 3 then
        return nil
    end

    for i = 1, #route - 2 do
        local p, p1, p2 = route[i], route[i + 1], route[i + 2]
        local w, w1 = unitVector(p1, p), unitVector(p1, p2)
        local w21 = unitVector(p, p1)
        local L = _w / 2 * 0.00001 / ((1-(w[1]*w1[1]+w[2]*w1[2])/ ((w[1]^2+w[2]^2)*(w1[1]^2+w1[2]^2)) ^ 0.5) / 2) ^ 0.5
        local a1 = ((w[2] - w1[2])^2 / ((w[2] - w1[2])^2 + (w[1] - w1[1])^2))^ 0.5 * L
        local a2 = (w[1] - w1[1]) * a1 / (w1[2] - w[2])
        if i == 1 then
            table.insert(subRoutes[1], p)
            table.insert(subRoutes[2], p)
            table.insert(subRoutes[3], p)
            table.insert(subRoutes[4], p)
        end
        local p1l, p1r = addOffset(p1, {["lng"] = -a2, ["lat"] = -a1}), addOffset(p1, {["lng"] = a2, ["lat"] = a1})
        local pl, pr = unitVector(p, p1l), unitVector(p, p1r)

        local direction1 = xProduct(pl, w21)
        local direction2 = xProduct(pr, w21)

        local p1lu, p1ru = addOffset(p1l, {["alt"] = 50}), addOffset(p1r, {["alt"] = 50})
        if direction1 > 0 then
            table.insert(subRoutes[1], p1l)
            table.insert(subRoutes[2], p1r)
            table.insert(subRoutes[3], p1lu)
            table.insert(subRoutes[4], p1ru)
        else
            table.insert(subRoutes[1], p1r)
            table.insert(subRoutes[2], p1l)
            table.insert(subRoutes[3], p1ru)
            table.insert(subRoutes[4], p1lu)
        end
        if i == #route - 2 then
            table.insert(subRoutes[1], p2)
            table.insert(subRoutes[2], p2)
            table.insert(subRoutes[3], p2)
            table.insert(subRoutes[4], p2)
        end
    end

    return subRoutes
end

function vectorIncludedAngle(vec1, vec2)
    if #vec1 ~= #vec2 then
        return nil
    end
    local value = 0

    if #vec1 == 2 then
        value = vectorDotProduct(vec1, vec2)/ ( vectorModule(vec1) * vectorModule(vec2))
    end

    return math.deg(math.acos(value))
end

function windSpeedOneDirection(angle, g, G)
        -- local radion = 3.14 / 180 * angle
    angle = math.abs(angle)

    return (-0.1524 + (0.1524^2 + 4 * 0.0079 * (g + G) * math.tan(math.rad(angle)))^0.5) / (2 * 0.0079)
end

function windSpeedCombine(angle1, angle2, g, G)
    return (windSpeedOneDirection(angle1, g, G)^2 + windSpeedOneDirection(angle2, g, G)^2)^0.5
end

function triCombine(x, y, z)
    return (x^2 + y^2 + z^2)^0.5
end

function weightCoefficient(angle1, angle2, g, G)
    local v = windSpeedCombine(tonumber(angle1), tonumber(angle2), g, G)
    -- ngx.log(ngx.ERR, v)
    return 1e-3 * (0.582 * v^2 - 6.2* v + 1581) - 0.59
    -- body
end

function takeoffLandingBattery(ax, xl, WE)
    return xl * 1000 / (-0.089 * xl ^ 3 + 1.1532 * xl ^ 2 - 5.7839 * xl + 17.147) * ax + WE + 30
end

function routeBattery(ax, xl, WE, flightTimeDetail)

    local battery = 0
    for k,detail in pairs(flightTimeDetail.route) do
        local wc = 20.682125 * detail.c ^ 3 - 15.92373 * detail.c^2 + 118.8195 * detail.c + (0.402 * detail.v ^3 - 6.2816 * detail.v ^2 + 11.0257 * detail.v) + xl * 1000 / (-0.089 * xl ^ 3 + 1.1531 * xl ^ 2 - 5.7839 * xl + 17.147)* ax + WE + 30
        -- ngx.log(ngx.ERR, wc)

        battery = battery + wc * detail.t
    end
    return battery
end

function estimateBatteryAfterFlight(gt, flightTimeDetail, y)
    -- ngx.log(ngx.ERR, y)
    -- ngx.log(ngx.ERR, flightTime)
    -- ngx.log(ngx.ERR, gt)
    local ax, WE = 6, 30
    local xl = gt / ax
    local _routeBattery,_takeoffLandingBattery = 0, 0
    local W = 900
    _routeBattery = routeBattery(ax, xl, WE, flightTimeDetail)
    ngx.log(ngx.ERR, _routeBattery)
    _takeoffLandingBattery = takeoffLandingBattery(ax, xl, WE)
    ngx.log(ngx.ERR, _takeoffLandingBattery)
    ngx.log(ngx.ERR, flightTimeDetail.takeoff.t)
    ngx.log(ngx.ERR, flightTimeDetail.takeoff.t)
    return y/100 - (_routeBattery + _takeoffLandingBattery * (flightTimeDetail.takeoff.t + flightTimeDetail.landing.t)) / 3600 / W
end

function maxWeight(x, G, L)
    local y = remainBatteryPercent(x) / 100
    if y < 0.2 then
        return 0
    end
    G = G * 1000
    -- ngx.log(ngx.ERR, y)
    -- ngx.log(ngx.ERR, G)
    -- ngx.log(ngx.ERR, (L / 13 /3600 / (y - 0.2) / (930 - 60) + 1.04/3000))
    return math.min(5000, math.max(0, 14 / (L / 13 /3600 / (y - 0.2) / (930 - 60) + 1.04/3000) - G))
end

function calcStartPointTimeDistance(p)
    local startPoint = p
    -- ngx.log(ngx.ERR, "takeoff"..t)
    if not startPoint.v or startPoint.v == 0 then
        startPoint.v = 1
    end
    local t = startPoint.alt >= 10 and (startPoint.alt - 10 / startPoint.v) + 10 or startPoint.alt
    -- ngx.log(ngx.ERR, "takeoff"..t)

    local d = startPoint.alt
    return t, d
end

function calcEndPointTimeDistance(p, heightDiff)
    local endPoint = p
    local t, d = 0, 0
    local landingMeter = math.abs(endPoint.alt - heightDiff)
    if landingMeter > 35 then
        t = t + (landingMeter - 35) / 1
        landingMeter = 35
    end

    if landingMeter > 15 and landingMeter <= 35 then
        t = t + (landingMeter - 15) / 0.7
        landingMeter = 15
    end

    if landingMeter <= 15 then
        t = t + landingMeter / 0.35 
    end
        -- ngx.log(ngx.ERR, "landing"..t)

    d = d + endPoint.alt - heightDiff

    return t, d
end

function flightTime(route, heightDiff)
    local t = 0
    local d = 0
    heightDiff = heightDiff or 0
    if not route or #route == 0 then
        return t,d
    end 
    -- ngx.log(ngx.ERR, #route)
    local st, sd = calcStartPointTimeDistance(route[1])
    t = t + st
    d = d + sd
    -- ngx.log(ngx.ERR, st)
    local et, ed = calcEndPointTimeDistance(route[#route], heightDiff)
        -- ngx.log(ngx.ERR, et)

    t = t + et
    d = d + ed
    for i = 1, #route - 1 do
        local p = route[i]
        local p1 = route[i + 1]
        -- ngx.log(ngx.ERR, route[i])
        local time, distance = timeDistanceBetweenRoutePoint(p, p1)
        -- ngx.log(ngx.ERR, time)
        t = t + time
        d = d + distance
    end
    -- ngx.log(ngx.ERR, "landing"..t)

    return t, d
end

function flightTimeDetail(route, heightDiff)
    local res = {}
    local t = 0
    local c = 0
    local d = 0
    heightDiff = heightDiff or 0
    if not route or #route == 0 then
        return t,d
    end 
    -- ngx.log(ngx.ERR, #route)
    local st, sd = calcStartPointTimeDistance(route[1])
    t = t + st
    d = d + sd
    res.takeoff = {["t"] = st, ["d"] = sd}
    -- ngx.log(ngx.ERR, st)
    local et, ed = calcEndPointTimeDistance(route[#route], heightDiff)
        -- ngx.log(ngx.ERR, et)
    res.landing = {["t"] = et, ["d"] = ed}
    t = t + et
    d = d + ed
    res.route = {}
    for i = 1, #route - 1 do
        local p = route[i]
        local p1 = route[i + 1]
        -- ngx.log(ngx.ERR, route[i])
        local time, distance, rateOfClimb = timeDistanceBetweenRoutePoint(p, p1)
        -- ngx.log(ngx.ERR, time)
        table.insert(res.route, {["t"] = time, ["d"] = distance, ["c"] = rateOfClimb, ["v"] = p1.v})

        t = t + time
        d = d + distance
    end
    -- ngx.log(ngx.ERR, "landing"..t)
    res.total = {["t"] = t, ["d"] = d}
    return res
end

function getThrottle(totalWeight, v)
    ngx.log(ngx.ERR, v)
    local v = 0.005447 * v ^ 3 - 0.7611478 * v ^ 2 + 36.43547 * v - 550.74
    ngx.log(ngx.ERR, v)
    local res = (-0.001149*totalWeight + 0.000819) * v + 0.0846 * totalWeight + 0.1761
    return res
end

function sortVersionId(tb)
    local function getTb(a)
        if type(a) == "table" then
            a = a.version_id
        end
        a = split(a, ".")
        return a
    end
    return table.sort(tb, function (a, b)
        a = getTb(a)
        b = getTb(b)
        ngx.log(ngx.ERR, json.encode(a))
        ngx.log(ngx.ERR, json.encode(b))
        a[1] = tonumber(a[1]) or 0
        b[1] = tonumber(b[1]) or 0
        a[2] = tonumber(a[2]) or 0
        b[2] = tonumber(a[2]) or 0
        a[3] = tonumber(a[3]) or 0
        b[3] = tonumber(a[3]) or 0

        return a[1] > b[1] or a[2] > b[2] or a[3] > b[3]
    end)
end

function getActualWeight(throttle, volt, g)
    return (throttle - 0.0044 * volt + 0.0143) / (0.0015 * volt + 0.1031) - g
end

function timeCanFly(g, G, flightTime)
    g = g * 1000
    G = G * 1000
    local totalW = 900
    local whl = (0.00002153 *((g+G)/6)^2+ 0.0568 *(g+G)/6 + 3.2)*6+30
    return 920 / whl * 3600 - flightTime
end

function modeToDesc(mode)
    local modes = {["0"]="离线", ["1"]="上线",["2"]="装订",["52"]= "待⻜",["3"] = "起⻜", ["4"] = "爬升", ["5"]= "航路", ["55"] = "识别", ["56"]="对准",["6"]= "下降", ["65"]= "降落", ["7"] = "调整航向",["11"]= "着陆",["12"]="紧急停桨",["13"] = "⾃动停桨",["14"] = "导引着陆", ["18"]= "完毕", ["20"] = "悬停等待", ["21"] = "暂停", ["22"]= "地⾯面导引", ["30"] = "应急航路",
    ["31"]="应急下降", ["32"] = "应急着陆", ["33"] = "⼈⼯操控", ["34"]= "遥控", ["35" ]= "失控", ["36"] = "应急装订"}
    return modes[""..mode]
end

function dec_hex(IN)
    local B,K,OUT,I,D=16,"0123456789ABCDEF","",0
    while IN>0 do
        I=I+1
        IN,D=math.floor(IN/B),math.mod(IN,B)+1
        OUT=string.sub(K,D,D)..OUT
    end
    return OUT
end

function getImageData(data)
    local p = 0
    while data and #data > 2 and p < #data - 2 do
        local this16 = xbit.readBuff(_uint16_t, data, p)
        -- ngx.log(ngx.ERR, this16)
        if this16 == 0xE2FF then
            ngx.log(ngx.ERR, p)
            break
        end
        p = p + 2
    end
    -- ngx.log(ngx.ERR, p)
    -- local len = xbit.readBuff(_uint32_t, data, p + 18 + 2)
    p = p + 18
    local offset = xbit.readBuff(_uint32_t, data, p + 8)
    ngx.log(ngx.ERR, offset)
    local imageInfo = {}
    p = p + offset
    imageInfo.time_std_s = xbit.readBuff(_uint32_t, data, p) or 0
    ngx.log(ngx.ERR, "time_std_s " .. imageInfo.time_std_s)
    p = p + 4
    imageInfo.lat_gps = xbit.readBuff(_int32_t, data, p) / 1e7 or 0
    ngx.log(ngx.ERR, "lat_gps " .. imageInfo.lat_gps)

    p = p + 4
    imageInfo.lon_gps = xbit.readBuff(_int32_t, data, p) / 1e7 or 0
    ngx.log(ngx.ERR, "lon_gps " .. imageInfo.lon_gps)
    p = p + 4
    imageInfo.alt_rel_pres = xbit.readBuff(_int32_t, data, p) / 1e3 or 0
    ngx.log(ngx.ERR, "alt_rel_pres " .. imageInfo.alt_rel_pres)
    p = p + 4
    imageInfo.vx = xbit.readBuff(_float, data, p)or 0
    ngx.log(ngx.ERR, "vx " .. imageInfo.vx)
    p = p + 4
    imageInfo.vy = xbit.readBuff(_float, data, p)or 0
    ngx.log(ngx.ERR, "vy " .. imageInfo.vy)
    p = p + 4
    imageInfo.vz = xbit.readBuff(_float, data, p) or 0
    ngx.log(ngx.ERR, "vz " .. imageInfo.vz)
    p = p + 4
    imageInfo.relative_alt = xbit.readBuff(_int32_t, data, p) / 1e3 or 0
    ngx.log(ngx.ERR, "relative_alt " .. imageInfo.relative_alt)
    p = p + 4
    imageInfo.pitch_deg = xbit.readBuff(_int16_t, data, p) / 10 or 0
    ngx.log(ngx.ERR, "pitch_deg " .. imageInfo.pitch_deg)
    p = p + 2
    imageInfo.roll_deg = xbit.readBuff(_int16_t, data, p) / 10 or 0
    ngx.log(ngx.ERR, "roll_deg " .. imageInfo.roll_deg)
    p = p + 2
    imageInfo.hdg = xbit.readBuff(_uint16_t, data, p) / 100 or 0
    ngx.log(ngx.ERR, "hdg " .. imageInfo.hdg)
    p = p + 2
    imageInfo.actuator_throttle = xbit.readBuff(_float, data, p) or 0
    ngx.log(ngx.ERR, "actuator_throttle " .. imageInfo.actuator_throttle)
    p = p + 4
    imageInfo.actuator_pitch = xbit.readBuff(_float, data, p) or 0
    ngx.log(ngx.ERR, "actuator_pitch " .. imageInfo.actuator_pitch)
    p = p + 4
    imageInfo.actuator_roll = xbit.readBuff(_float, data, p) or 0
    ngx.log(ngx.ERR, "actuator_roll " .. imageInfo.actuator_roll)
    p = p + 4
    imageInfo.actuator_yaw = xbit.readBuff(_float, data, p) or 0
    ngx.log(ngx.ERR, "actuator_yaw " .. imageInfo.actuator_yaw)
    p = p + 4
    return imageInfo
end

local mt = {
    __newindex = function (table, key, val)
        error('attempt to write to undeclared variable "' .. key .. '"')
    end
}
setmetatable(_M, mt)

